package xzk

import (
	"errors"
	"fmt"

	"github.com/samuel/go-zookeeper/zk"
)

type ElectionManager struct {
	zkPool     *Pool
	IsMasterQ  chan bool
	rootPath   string
	masterPath string
}

func NewElectionManager(isMasterQ chan bool, rootPath, masterPath string) *ElectionManager {
	return &ElectionManager{
		pool,
		isMasterQ,
		rootPath,
		masterPath,
	}
}

func (e *ElectionManager) Run() {
	err := e.electMaster()
	if err != nil {
		fmt.Println(fmt.Sprintf("elect master(%v) err:%v ", e.masterPath, err))
	}

	e.watchMaster()
}

// 选举master
func (e *ElectionManager) electMaster() error {
	if e.zkPool == nil {
		return errors.New("zk pool is nil")
	}

	err := e.zkPool.initConnection()
	if err != nil {
		return err
	}

	isExist, _, err := e.zkPool.zKClientConn.Exists(e.rootPath)
	if err != nil {
		return err
	}
	if !isExist {
		path, err := e.zkPool.zKClientConn.Create(e.rootPath, nil, 0, zk.WorldACL(zk.PermAll))
		if err != nil {
			return err
		}

		if e.rootPath != path {
			return errors.New("Create returned different path " + e.rootPath + " != " + path)
		}
	}

	masterPath := e.rootPath + e.masterPath
	path, err := e.zkPool.zKClientConn.Create(masterPath, nil, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
	if err != nil {
		fmt.Println(fmt.Sprintf("elect master(%v) err:%v ", e.masterPath, err))
		e.IsMasterQ <- false
		return err
	}

	if path == masterPath {
		fmt.Println(fmt.Sprintf("elect master(%v) success", e.masterPath))
		e.IsMasterQ <- true
		return nil
	}

	return errors.New("Create returned different path " + masterPath + " != " + path)
}

// 监听zookeeper中master znode，若被删除，表示master重启，故障或网络迟缓，重新选举
func (e *ElectionManager) watchMaster() {
	for {
		children, state, childCh, err := e.zkPool.zKClientConn.ChildrenW(e.rootPath + e.masterPath)
		if err != nil {
			fmt.Println("watch children error, ", err)
		} else {
			fmt.Println("watch children result, ", children, state)
		}

		select {
		case childEvent := <-childCh:
			if childEvent.Type == zk.EventNodeDeleted {
				fmt.Println("receive znode delete event, ", childEvent)
				fmt.Println("start elect new master ...")

				if err := e.electMaster(); err != nil {
					fmt.Println(fmt.Sprintf("elect new master(%v) err:%v ", e.masterPath, err))
				}
			}
		}
	}
}
